package picsim;

import java.io.*;
import java.util.*;
import gnu.io.*;

/**
 * 
 * @author Daniel
 * @brief Klasse, die die Steuerung und Kommunikation mit der RS232-Schnittstelle implementiert.
 */
public class Comport {

    private static CommPortIdentifier portId;
    private static Enumeration portList;
    private SerialPort sp = null;
    private InputStream input = null;
    private OutputStream output = null;

    private String port = "";
    private String inputPortA = "";
    private String inputPortB = "";
    private boolean reset;

    // data for communication with serial port
    // for documentation see "PicViewDoc.pdf"
    private final int baud = 4800;
    private final int databit= SerialPort.DATABITS_8;
    private final int stopbit= SerialPort.STOPBITS_1;
    private final int parity = SerialPort.PARITY_NONE;
    

    /**
     *
     * @param port (String)
     * @brief Konstruktor der Klasse Comport. Startet die Verbindung mit der seriellen Schnittstelle.
     */
    public Comport(String port)    {

        this.port = port;
        portList = CommPortIdentifier.getPortIdentifiers();
        
        while (portList.hasMoreElements()) {

            portId = (CommPortIdentifier) portList.nextElement();
            
            try {
                if(portId.getName().equals(port))   {
                    // initialize connection with comport...
                    sp = (SerialPort) portId.open("serial",1000);
                    sp.setSerialPortParams(baud,databit,stopbit,parity);
                }
            }
            catch(PortInUseException e) {
               System.out.println("Port wird bereits benutzt!");
            }
            catch(UnsupportedCommOperationException e) {
                // ...
            }
            
        }
        
    }

    /**
     * 
     * @param trisA (String)
     * @param portA (String)
     * @param trisB (String)
     * @param portB (String)
     * @brief Kommunikation mit der seriellen Schnittstelle abhandeln
     */
    public void communicateWithCom(String trisA, String portA, String trisB, String portB) {

        // fill to two-digit hex number
        if(trisA.length() < 2)   {
            trisA = "0" + trisA;
        }
        if(portA.length() < 2)   {
            portA = "0" + portA;
        }
        if(trisB.length() < 2)   {
            trisB = "0" + trisB;
        }
        if(portB.length() < 2)   {
            portB = "0" + portB;
        }
        // ************
        
        char[] trisAchar = parseHexToAscii(trisA.toCharArray());
        char[] portAchar = parseHexToAscii(portA.toCharArray());
        char[] trisBchar = parseHexToAscii(trisB.toCharArray());
        char[] portBchar = parseHexToAscii(portB.toCharArray());

        // get numeric values of "high" and "low"
        
        // TRISA / PORTA
        int trisAhigh = (int)trisAchar[0];
        int trisAlow = (int)trisAchar[1];

        int portAhigh = (int)portAchar[0];
        int portAlow = (int)portAchar[1];
        
        // TRISB / PORTB
        int trisBhigh = (int)trisBchar[0];
        int trisBlow = (int)trisBchar[1];
        
        int portBhigh = (int)portBchar[0];
        int portBlow = (int)portBchar[1];

        // communicate with rs232!!
        try {                   
            output = sp.getOutputStream();
            input = sp.getInputStream();
        }
        catch (IOException ex) {
            //ex.printStackTrace();
        }

        try {

            //
            //         OUTPUT
            //

            // send information to comport
            // TRISB / PORTB
            output.write(trisAhigh);
            output.write(trisAlow);

            output.write(portAhigh);
            output.write(portAlow);
            // ************

            // TRISB / PORTB
            output.write(trisBhigh);
            output.write(trisBlow);

            output.write(portBhigh);
            output.write(portBlow);
            // ************
            
            output.write(13); //Carriage Return = CR
            
            output.flush();
            output.close();


            //
            //         INPUT
            //

            // get answer from comport convert to hex-number
            //PortA
            char inputPortAhigh = parseAsciiToHex((char)input.read());
            char inputPortAlow = parseAsciiToHex((char)input.read());

            //PortB
            char inputPortBhigh = parseAsciiToHex((char)input.read());
            char inputPortBlow = parseAsciiToHex((char)input.read());

            int last = input.read();

            input.close();

            // reset?
            if(inputPortAhigh == '1' || inputPortAhigh == '0')
                reset = true;
            else
                reset = false;


            // connect information of high and low
            String test_portA = String.valueOf(inputPortAhigh) + String.valueOf(inputPortAlow);

            // clear "error" of comport (clear of ext. board)
            try {
                int test_pA = Integer.parseInt(test_portA, 16);
             
                if(test_pA > 0x1F) {
                    test_pA = test_pA - 32;
                    inputPortA = Integer.toHexString(test_pA);
                }
                else {
                    inputPortA = String.valueOf(inputPortAhigh) + String.valueOf(inputPortAlow);
                }
            }
            catch(NumberFormatException ex) {
                System.out.println("Falscher input");
            }
            
            inputPortB = String.valueOf(inputPortBhigh) + String.valueOf(inputPortBlow);
            
        } catch (IOException ex) {
            //ex.printStackTrace();
        }        

    }

    /**
     *
     * @return inputPortA (String)
     * @brief Gibt die durch die Kommunikation ermittelte Information von PortA zurück
     */
    public String getInputPortA()   {
        return inputPortA;
    }

    /**
     *
     * @return inputPortB (String)
     * @brief Gibt die durch die Kommunikation ermittelte Information von PortB zurück
     */
    public String getInputPortB()   {
        return inputPortB;
    }

    /**
     * 
     * @return true | false 
     * @brief Liefert die Information, ob ein Reset gesendet wurde.
     */
    public boolean resetPushed()    {
        return reset;
    }

    /**
     * @brief Schließt die Verbindung der seriellen Schnittstelle
     */
    public void closeComConnection()    {
        sp.close();
    }
    
    /**
     *
     * @param c (char)
     * @return c (char)
     * @brief Konvertiert das übergebene ASCII-Zeichen in das entsprechene Hex-Zeichen.
     */
    private char parseAsciiToHex(char c)    {

             if(c == '?') c = 'f';
        else if(c == '>') c = 'e';
        else if(c == '=') c = 'd';
        else if(c == '<') c = 'c';
        else if(c == ';') c = 'b';
        else if(c == ':') c = 'a';
                     else c = c;
        
             return c;

    }

    /**
     *
     * @param cArray (char[])
     * @return cArray (char[])
     * @brief Konvertiert die Hex-Zeichen in das entsprechende ASCII-Zeichen.
     */
    private char[] parseHexToAscii(char[] cArray)   {

        for(int i=0; i<cArray.length; i++)  {

                 if(cArray[i] == 'A' || cArray[i] == 'a')  cArray[i] = ':';
            else if(cArray[i] == 'B' || cArray[i] == 'b')  cArray[i] = ';';
            else if(cArray[i] == 'C' || cArray[i] == 'c')  cArray[i] = '<';
            else if(cArray[i] == 'D' || cArray[i] == 'd')  cArray[i] = '=';
            else if(cArray[i] == 'E' || cArray[i] == 'e')  cArray[i] = '>';
            else if(cArray[i] == 'F' || cArray[i] == 'f')  cArray[i] = '?';
            
        }

        return cArray;

    }
    
}